/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Code } from './code';
import { allocateStack } from './allocate_stack';
import { createPayloadGbemuExecuteIteration } from './payload-gbemu-executeIteration';
import { createPayloadImagingGaussianBlurGaussianBlur } from './payload-imaging-gaussian-blur-gaussianBlur';
import { createPayloadTypescriptScanIdentifier } from './payload-typescript-scanIdentifier';
import { createPayloadAirJSACLj8C } from './payload-airjs-ACLj8C';

const ONE_THOUSAND: number = 1000;
const EX_BEGIN_NUM: number = 632653144;
const EX_END_NUM: number = 372715518;

const GBG_BEGIN_NUM: number = 3677819581;
const GBG_END_NUM: number = 1252116304;

const TS_BEGIN_NUM: number = 1914852601;
const TS_END_NUM: number = 837339551;

const AJA_BEGIN_NUM: number = 1373599940;
const AJA_END_NUM: number = 3981283600;
const LOOP_COUNT: number = 120;

class Payload {
  generate: () => Code;
  earlyHash: number;
  lateHash: number;
  name: string;

  constructor(generate: () => Code, earlyHash: number, lateHash: number, name: string) {
    this.generate = generate;
    this.earlyHash = earlyHash;
    this.lateHash = lateHash;
    this.name = name;
  }
}

/*
 * @State
 * @Tags Jetstream2
 */
export class Benchmark {
  _verbose = false;
  _payloads: Payload[];

  constructor() {
    this._payloads = [
      new Payload(createPayloadGbemuExecuteIteration, EX_BEGIN_NUM, EX_END_NUM, 'createPayloadGbemuExecuteIteration'),
      new Payload(createPayloadImagingGaussianBlurGaussianBlur, GBG_BEGIN_NUM, GBG_END_NUM, 'createPayloadImagingGaussianBlurGaussianBlur'),
      new Payload(createPayloadTypescriptScanIdentifier, TS_BEGIN_NUM, TS_END_NUM, 'createPayloadTypescriptScanIdentifier'),
      new Payload(createPayloadAirJSACLj8C, AJA_BEGIN_NUM, AJA_END_NUM, 'createPayloadAirJSACLj8C')
    ];
  }

  runIteration(): void {
    for (let payload of this._payloads) {
      // Sadly about 17% of our time is in generate. I don't think that's really avoidable,
      // and I don't mind testing VMs' ability to run such "data definition" code quickly. I
      // would not have expected it to be so slow from first principles!

      let code = payload.generate();

      let hash = code.hash();

      if (hash !== payload.earlyHash) {
        throw new Error(`Wrong early hash for ${payload.name}: ${hash}`);
      }
      allocateStack(code);

      hash = code.hash();
      if (hash !== payload.lateHash) {
        throw new Error(`Wrong late hash for ${payload.name}: ${hash}`);
      }
    }
  }

  /*
   * @Benchmark
   */
  run(): void {
    let startTime = ArkTools.timeInUs() / ONE_THOUSAND;
    // let startTime = Date.now() * ONE_THOUSAND
    for (let i = 0; i < LOOP_COUNT; i++) {
      this.runIteration();
    }
    let endTime = ArkTools.timeInUs() / ONE_THOUSAND;
    // let endTime = Date.now() * ONE_THOUSAND
    print('air: ms = ' + (endTime - startTime));
  }

}

declare interface ArkTools {
  timeInUs(args: number): number;
}


let loopCountForPreheat = 1;
for (let i = 0; i < loopCountForPreheat; i++) {
  new Benchmark().run();
}

ArkTools.waitAllJitCompileFinish();

new Benchmark().run();
